#include <sys/types.h>

#include "url.h"

static const char *HEXCHARS = "0123456789ABCDEF";

#ifdef  __cplusplus
#include <cctype>

extern "C" {
#endif

static int htoi(const char *s)
{
	int value;
	register int c;

	c = ((unsigned char *)s)[0];
	if (isupper(c))
		c = tolower(c);
	value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
	c = ((unsigned char *)s)[1];
	if (isupper(c))
		c = tolower(c);
	value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
	return (value);
}

int urlencode(char* dest, const char *src, const int len)
{
	register unsigned char c;
	const char *p;
	char *to;
	to = dest;
	for (p = src; p < src + len; ++p, ++to) {
		c = *p;
		if (c == ' ') {
			*to = '+';
#ifndef CHARSET_EBCDIC
		} else if ((c < '0' && c != '-' && c != '.') ||
				   (c < 'A' && c > '9') ||
				   (c > 'Z' && c < 'a' && c != '_') ||
				   (c > 'z')) {
			to[0] = '%';
			to[1] = HEXCHARS[c >> 4];
			to[2] = HEXCHARS[c & 15];
			to += 2;
#else //CHARSET_EBCDIC
		} else if (!isalnum(c) && strchr("_-.", c) == NULL) {
			/* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
			to[0] = '%';
			to[1] = HEXCHARS[os_toascii[c] >> 4];
			to[2] = HEXCHARS[os_toascii[c] & 15];
			to += 2;
#endif //CHARSET_EBCDIC
		} else {
			*to = c;
		}
	}
	*to = 0;
	return (to - dest);
}

int urldecode(char* dest, const char *src, const int len)
{
	char *to;
	const char *p;
	to = dest;
	p  = src;
	for (int i = len; i != 0; --i, ++p, ++to) {
		if (*p == '+') {
			*to = ' ';
		}
		else if (*p == '%' && i > 2
				&& isxdigit((int) *(p + 1))
				&& isxdigit((int) *(p + 2))) {
#ifndef CHARSET_EBCDIC
			*to = (char) htoi(p + 1);
#else
			*to = os_toebcdic[(char) htoi(p + 1)];
#endif
			p += 2;
			i -= 2;
		} else {
			*to = *p;
		}
		++p;
		++to;
	}
	*to = '\0';
	return (to - dest);
}

bool parse_url(const char *url, char *host, int *port, char *path, char **query)
{
	const char *ptr;
	int hostlen, numread = 0;

	/* go through the url */
	/* reset url to point PAST the http:// */
	/* assume it's always 7 chars! */
	ptr = url + 7;

	/* no http:// now... server is simply up to the next / or : */
	sscanf(ptr, "%255[^/:]", host);
	hostlen = strlen(host);

	/* get port */
	if(ptr[hostlen] != ':') *port = 80;
	else {
		sscanf(&ptr[hostlen+1], "%d%n", port, &numread);
		numread ++;
	}

	/* the ptr is a pointer into the rest of url */
	ptr += hostlen + numread;

	sscanf(ptr, "%255[^?]", path);
	*query = (char *)ptr + strlen(path);

	return true;
}

#ifdef  __cplusplus
}

bool urlencode(std::string& str, const char *s)
{
	return urlencode(str, s, strlen(s));
}

bool urlencode(std::string& str, const std::string& s)
{
	return urlencode(str, s.c_str(), s.size());
}

bool urlencode(std::string& str, const char *src, const int len)
{
	if (src == NULL) return false;
	register unsigned char c;
	const char *p;
	str.clear();
	for (p = src; p < src + len; ++p) {
		c = *p;
		if (c == ' ') {
			str += '+';
#ifndef CHARSET_EBCDIC
		} else if ((c < '0' && c != '-' && c != '.') ||
				   (c < 'A' && c > '9') ||
				   (c > 'Z' && c < 'a' && c != '_') ||
				   (c > 'z')) {
			str += '%';
			str += HEXCHARS[c >> 4];
			str += HEXCHARS[c & 15];
#else  //CHARSET_EBCDIC
		} else if (!isalnum(c) && strchr("_-.", c) == NULL) {
			/* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
			str += '%';
			str += HEXCHARS[os_toascii[c] >> 4];
			str += HEXCHARS[os_toascii[c] & 15];
#endif //CHARSET_EBCDIC
		} else {
			str += c;
		}
	}
	return true;
}

bool urldecode(std::string& str, const char* s)
{
	return urldecode(str, s, strlen(s));
}

bool urldecode(std::string& str, const std::string& s)
{
	return urldecode(str, s.c_str(), s.size());
}

bool urldecode(std::string& str, const char *src, const int len)
{
	if(src == NULL) return false;
	const char *p= src;
	str.clear();

	for(int i = len; i != 0; --i, ++p) {
		if (*p == '+') {
			str += ' ';
		}
		else if (*p == '%' && i > 2
				&& isxdigit((int) *(p + 1))
				&& isxdigit((int) *(p + 2))) {
#ifndef CHARSET_EBCDIC
			str += (char) htoi(p + 1);
#else
			str += os_toebcdic[(char) htoi(p + 1)];
#endif
			p += 2;
			i -= 2;
		} else {
			str += *p;
		}
	}

	return true;
}

bool parse_query(const char* query, const int size, std::map<std::string, std::string> &m)
{
	/* var r	last   forward pionter
	 * var e	middle forward pionter
	 * var s	first  forward pionter.
	 **/
	register const char *e, *r;
	register bool  equal_after = false;
	const char *s;

	std::string k, v;
	int i = 0;
	e = r = s = query;
	while(i++ < size && *e != '\0' && *r != '\0')
	{
		if(!equal_after)
		{
			/* skip if '&' or '=' at the begining*/
			if(e == s && (*e == '&' || *e == '=')) {
				s = ++e;
			}
			else if(*e == '&') {
				k.assign(s, e - s);
				m.insert(make_pair(k, ""));
				s = ++e;
			}
			else if(*e == '=') {
				r = e + 1;
				equal_after = true;
			}
			else {
				++e;
			}

			continue;
		}
	
		/* equal sign found, parse the second part
		 * equal_after == true **/
		if(*r == '&')
		{
			k.assign(s, e++ - s);
			if(e != r) {
				urldecode(v, std::string(e, r - e));
				m.insert(make_pair(k, v));
			}
			else
			{
				m.insert(make_pair(k, ""));
			}
	
			s = e = ++r;
			equal_after = false;
			continue;
		}
		++r;
	}

	if(s != e)
	{
		k.assign(s, e - s);
		if(r != query && --r - e > 0) {
			++e;
			urldecode(v, std::string(e, r - e));
			m.insert(make_pair(k, v));
		}
		else
		{
			m.insert(make_pair(k, ""));
		}
	}

#ifdef URL_DEBUG
	std::map<std::string, std::string>::iterator it;
	for(it = m.begin(); it!=m.end(); ++it)
		printf("key:%s\tvalue:%s  \n", it->first.c_str(), it->second.c_str());
#endif
	return !m.empty();
}

#endif
